package cn.bearspark.framework.redis.starter.buffer.list;

import cn.bearspark.framework.redis.starter.buffer.RedisBuffer;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * Redis 的 list 数据结构的基础缓存器
 *
 * @param <K> 在 Redis 中存储的数字的数据类型
 * @author f
 */
public abstract class RedisBaseListBuffer<K extends Number> extends RedisBuffer {

    /**
     * 设置空值缓存的值，默认是 -1
     */
    private static final Long ID_NULL_CACHE = -1L;

    /**
     * 设置 list 中空值缓存的有效期（单位：s），默认是 5s
     */
    private static final int NULL_CACHE_EXPIRE = 5;

    /**
     * 设置 list 中元素的缓存的有效期（单位：s），默认是 5min
     */
    private static final int CACHE_EXPIRE = 60 * 5;

    public RedisBaseListBuffer(RedisTemplate<String, Object> redisTemplate) {
        super(redisTemplate);
    }

    /**
     * 将指定对象转换成 K 类型的对象
     * <p>
     * 默认是转换成 Long 类型，如果子缓存器需要转换成 Integer 类型，则需要重写这个方法
     *
     * @param obj 指定对象
     * @return K 类型的对象
     */
    protected K convert(Object obj) {
        return (K) super.objToLong(obj);
    }

    /**
     * 获取指定键对应的 list 的指定范围内的元素
     *
     * @param key            指定的键
     * @param start          起始索引
     * @param end            终止索引
     * @param resultSupplier 当在缓存中查询不到对应的数据时，使用这个函数来获取数据
     * @return 指定键对应的 list 的指定范围内的元素
     */
    protected List<K> range(String key, int start, int end, Supplier<List<K>> resultSupplier) {
        List<K> resultList = Objects.requireNonNull(redisTemplate.opsForList().range(key, start, end))
                .stream().map(this::convert).collect(Collectors.toCollection(ArrayList::new));

        // 如果有缓存，考虑直接返回
        if (!resultList.isEmpty()) {
            // 如果是空值缓存，则返回空集合，空值缓存是 [-1]
            if (Objects.equals(resultList.get(0), ID_NULL_CACHE)) {
                return new ArrayList<>();
            }
            return resultList.stream().filter(result -> (Long) result != -1L).toList();
        }

        // 如果没有缓存，则查询数据库
        resultList = resultSupplier.get();

        // 如果查询结果为空，则需要添加空值缓存
        if (resultList.isEmpty()) {
            super.executePipelined(operations -> {
                operations.opsForList().rightPush(key, ID_NULL_CACHE);
                operations.expire(key, NULL_CACHE_EXPIRE, TimeUnit.SECONDS);
            });
            return resultList;
        }

        // 缓存查询结果
        List<K> finalResultList = resultList;
        super.executePipelined(operations -> {
            finalResultList.forEach(result ->
                    operations.opsForList().rightPush(key, result)
            );
            operations.expire(key, CACHE_EXPIRE, TimeUnit.SECONDS);
        });
        return resultList;
    }

    /**
     * 清理缓存
     *
     * @param keys 缓存的键
     */
    protected void clean(String... keys) {
        redisTemplate.delete(Arrays.stream(keys).toList());
    }

}
